import os
from os.path import exists
import json
import requests
import logging
import re
from urllib.parse import urljoin
from base import BASE_PATH
import multiprocessing
from pyquery import PyQuery as pq

# 定义日志
logging.basicConfig(level=logging.INFO,
                    format='%(asctime)s - %(levelname)s: %(message)s')

# 爬取的链接
BASE_URL = 'https://ssr1.scrape.center'
# 总的页数
TOTAL_PAGE = 10
# 存储目录
RESULIS_DIR = BASE_PATH + os.sep + 'results' + os.sep + 'spilder_movies'

"""
 爬取详情页
 通过基础爬虫实战01_爬起列表页，我们已经拿到所有详情页 URL  那么下一步当然就是解析详情页并提取出我们想要的信息了

 内容如下:
 正则表达式提取每部电影的名称、封面、类别、上映时间、评分、剧情简介等内容
"""


def request_scrape(url):
    """
    根据url 返回html源码
    :param url:
    :return:
    """
    logging.info('开始请求获取html源码,url: %s', url)
    try:
        headers = {
            'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36'
        }
        response = requests.get(url, headers=headers)
        if response.status_code == 200:
            html = response.text
            return html
        logging.error('请求状态码错误, code: %s', response.status_code)
    except requests.RequestException as e:
        # 错误信息日志-打印堆栈信息 exc_info = True
        logging.error('请求报错，url: %s', url, exc_info=True)


def scrape_index(page):
    """
     传入page 构建url
    :param page:
    :return:
    """
    url = urljoin(BASE_URL, f'/page/{page}')
    return url


def parse_index_get_detailurl(html):
    """
    解析html获取每一个列表页，详情页url
    :param html:
    :return:
    """
    # 需要正则捕获的超链接 <a data-v-7f856186="" href="/detail/1" class="name">
    pattern = re.compile('<a.*?href="(.*?)".*?class="name">')
    items = re.findall(pattern, html)
    if not items:
        return []
    # 循环遍历拼接url并返回，通过 yield 返回生成器(里面所有的url列表)
    for item in items:
        url = urljoin(BASE_URL, item)
        yield url


def parse_detail(html):
    """
    解析详情页html源码，提取需要的数据并返回
    :param html:
    :return:
    """
    doc = pq(html)
    item = doc('.item')
    cover = item.find('.cover').attr('src') if item.find('.cover') else None
    categories = item.find('.category span').text().split(' ') if item.find('.category span') else None
    name = item.find('h2').text() if item.find('h2') else None
    all_info__divs = item.find('.m-v-sm.info')
    last_div = all_info__divs.eq(1) #取第二个div
    # print('this', last_div)
    release_time = last_div.find('span').text() if last_div.find('span') else None
    score = item.find('.score').text() if item.find('.score') else None
    drama = item.find('.drama p').text() if item.find('.drama p') else None

    return {
        'cover': cover,
        'categories': categories,
        'name': name,
        'release_time': release_time,
        'score': score,
        'drama': drama,
    }


def save_files(data):
    """
        按名称存储json文件
    :param data:
    :return:
    """
    name = data.get('name')
    data_path = f'{RESULIS_DIR}/{name}.json'
    logging.info('存储json文件的路径: %s', data_path)
    with open(data_path, mode='w', encoding='utf-8') as f:
        json.dump(data, f, ensure_ascii=False, indent=2)


"""
由于整个的爬取是单进程的，而且只能逐条爬取，速度稍微有点慢，我们有没有方法来对整个爬取过程进行加速呢？
由于一共有 10 页列表页，这 10 页内容是互不干扰的，所以我们可以一页开一个进程来爬取。而且由于这 10 个列表页页码正好可以提前构造成一个列表，所以我们可以选用多进程里面的进程池 Pool 来实现这个过程。
"""

def run(page):
    # 构建列表页-url
    url = scrape_index(page)
    # 获取列表页html源码
    html = request_scrape(url)
    # 获取某一页列表，所有详情页url
    datail_urls = parse_index_get_detailurl(html)
    # logging.info("详情页url %s", list(datail_urls))
    # 解析每一个详情页
    for url in datail_urls:
        # 获取详情页源码
        html = request_scrape(url)
        data = parse_detail(html)
        logging.info("获取到详情页数据 %s", data)
        logging.info('存储json文件')
        save_files(data)
        logging.info('存储数据成功')


if __name__ == '__main__':
    # 测试执行
    # html = request_scrape('https://ssr1.scrape.center/detail/1')
    # # print(html)
    # data = parse_detail(html)
    # print(data)

    """
    这里我们首先给 main 方法添加了一个参数 page，用以表示列表页的页码。接着我们声明了一个进程池，并声明了 pages 为所有需要遍历的页码，即 1-10。最后调用 map 方法，第一个参数就是需要被调用的参数，第二个参数就是 pages，即需要遍历的页码。

这样 pages 就会被依次遍历，把 1-10 这 10 个页码分别传递给 run 方法，并把每次的调用变成一个进程，加入到进程池中执行，进程池会根据当前运行环境来决定运行多少进程。比如我的机器的 CPU 有 8 个核，那么进程池的大小会默认设定为 8，这样就会同时有 8 个进程并行执行。

运行输出结果和之前类似，但是可以明显看到加了多进程执行之后，爬取速度快了非常多。可以清空一下之前的爬取数据，可以发现数据依然可以被正常保存成 JSON 文件。
    """
    #如果存储目录不存在，则创建文件夹目录
    if not exists(RESULIS_DIR):
        os.makedirs(RESULIS_DIR)

    # 创建连接池对象
    pool = multiprocessing.Pool()
    # 这里pages是一个整数序列（比如1,2,3,...），pool.map会把这些整数依次传给main函数的page参数。也就是说，run函数接收到的是数字类型的page参数
    pages = range(1, TOTAL_PAGE + 1)
    pool.map(run, pages)
    pool.close()  # 关闭进程池，不再接受新的任务
    pool.join()  # 主进程等待所有子进程结束
